1 module hip.util.conv; 2 import hip.util.string; 3 import hip.util.reflection:isArray; 4 public import hip.util.to_string_range; 5 public import hip.util.string:toStringz; 6 7 8 string toString(dstring dstr) pure nothrow @safe 9 { 10 try 11 { 12 string ret; 13 foreach(ch; dstr) 14 ret~= ch; 15 return ret; 16 } 17 catch(Exception e){return "";} 18 } 19 20 string toString(char[] arr) pure nothrow @trusted @nogc {return cast(string)arr;} 21 string toString(T)(T[] arr) pure nothrow @safe if(!is(T == char)) 22 { 23 string ret = "["; 24 for(int i = 0; i < arr.length; i++) 25 { 26 if(i) 27 ret~= ","; 28 ret~= toString(arr[i]); 29 } 30 return ret~"]"; 31 } 32 33 34 string toString(T)(T structOrTupleOrEnum) pure nothrow @safe if(!isArray!T) 35 { 36 static if(is(T == struct))//For structs declaration 37 { 38 import hip.util.reflection; 39 static if(__traits(hasMember, T, "toString") && hasUDA!(__traits(getMember, T, "toString"), "format")) 40 { 41 import hip.util.format; 42 return formatFromType(structOrTupleOrEnum); 43 } 44 else 45 { 46 alias struct_ = structOrTupleOrEnum; 47 string s = "("; 48 static foreach(i, alias m; T.tupleof) 49 { 50 if(i > 0) 51 s~= ", "; 52 s~= to!string(struct_.tupleof[i]); 53 } 54 return T.stringof~s~")"; 55 } 56 } 57 else static if(is(T == enum)) 58 { 59 foreach(mem; __traits(allMembers, T)) 60 if(__traits(getMember, T, mem) == structOrTupleOrEnum) 61 return T.stringof~"."~mem; 62 return T.stringof~".|MEMBER_NOT_FOUND|"; 63 } 64 else static if(__traits(isAssociativeArray, T)) 65 { 66 string s; 67 try 68 { 69 foreach(key, value; structOrTupleOrEnum) 70 { 71 s~= "\n\t"~key.to!string~": "~value.to!string; 72 } 73 } 74 catch(Exception e){} 75 return "["~s~"\n]"; 76 } 77 // else static if(isD) 78 else static assert(0, "Not implemented for "~T.stringof); 79 // static if(isTuple!T) 80 // { 81 // alias tupl = structOrTupleOrEnum; 82 // string ret; 83 // foreach(i, v; tupl) 84 // { 85 // if(i > 0) 86 // ret~= ", "; 87 // ret~= to!string(v); 88 // } 89 // return T.stringof~"("~ret~")"; 90 // } 91 // else 92 } 93 94 95 96 string dumpStructToString(T)(T struc) if(is(T == struct)) 97 { 98 string s = "\n("; 99 static foreach(i, alias m; T.tupleof) 100 { 101 s~= "\n\t "~m.stringof~": "; 102 s~= toString(struc.tupleof[i]); 103 } 104 return T.stringof~s~"\n)"; 105 } 106 107 108 T toStruct(T)(string struc) pure nothrow 109 { 110 T ret; 111 string[] each; 112 string currentArg; 113 114 bool isInsideString = false; 115 for(size_t i = 1; i < (cast(int)struc.length)-1; i++) 116 { 117 if(!isInsideString && struc[i] == ',') 118 { 119 each~= currentArg; 120 currentArg = null; 121 if(struc[i+1] == ' ') 122 i++; 123 continue; 124 } 125 else if(struc[i] == '"') 126 { 127 isInsideString = !isInsideString; 128 continue; 129 } 130 currentArg~= struc[i]; 131 } 132 if(currentArg.length != 0) 133 each~=currentArg; 134 135 static foreach(i, m; __traits(allMembers, T)) 136 {{ 137 alias member = __traits(getMember, ret, m); 138 member = to!(typeof(member))(each[i]); 139 }} 140 return ret; 141 } 142 143 144 bool toBool(string str) pure nothrow @safe @nogc {return str == "true";} 145 146 ///Use that for making toStruct easier 147 string toString(string str) pure nothrow @safe @nogc {return str;} 148 149 150 string toString(bool b) pure nothrow @safe @nogc 151 { 152 return b ? "true" : "false"; 153 } 154 155 TO to(TO, FROM)(FROM f) pure nothrow 156 { 157 static if(is(TO == string)) 158 { 159 static if(is(FROM == const(char)*) || is(FROM == char*)) 160 return fromStringz(f); 161 else static if(is(FROM == enum)) 162 return toString!FROM(f); 163 else 164 return toString(f); 165 } 166 else static if(is(TO == int) || is(TO == long)) 167 { 168 static if(!is(FROM == string)) 169 return toInt(f.toString); 170 else 171 return cast(TO)toInt(f); 172 } 173 else static if(is(TO == uint) || is(TO == size_t) || is(TO == ubyte) || is(TO == ushort)) 174 { 175 static if(!is(FROM == string)) 176 return cast(TO)toInt(f.toString); 177 else 178 return cast(TO)toInt(f); 179 } 180 else static if(is(TO == float) || is(TO == double)) 181 { 182 static if(!is(FROM == string)) 183 return toFloat(f.toString); 184 else 185 return toFloat(f); 186 } 187 else static if(is(TO == bool)) 188 { 189 static if(!is(FROM == string)) 190 return toBool(f.toString); 191 else 192 return toBool(f); 193 } 194 else 195 { 196 static if(!is(FROM == string)) 197 return toStruct!TO(f.toString); 198 else 199 return toStruct!TO(f); 200 } 201 } 202 203 /// This function can be called at compilation time without bringing runtime 204 string toString(long x) pure nothrow @safe 205 { 206 enum numbers = "0123456789"; 207 bool isNegative = x < 0; 208 if(isNegative) 209 x*= -1; 210 size_t div = 10; 211 int length = 1; 212 int count = 1; 213 while(div <= x) 214 { 215 div*=10; 216 length++; 217 } 218 if(isNegative) length++; 219 char[] ret = new char[](length); 220 if(isNegative) 221 ret[0] = '-'; 222 div = 10; 223 while(div <= x) 224 { 225 count++; 226 ret[length-count]=numbers[cast(size_t)((x/div)%10)]; 227 div*=10; 228 } 229 ret[length-1] = numbers[cast(size_t)(x%10)]; 230 return ret[0..$]; 231 } 232 233 234 string toString(float f) pure nothrow @safe 235 { 236 if(f != f) return "nan"; 237 else if(f == -float.infinity) return "-inf"; 238 else if(f == float.infinity) return "inf"; 239 240 bool isNegative = f < 0; 241 if(isNegative) 242 f = -f; 243 244 float decimal = f - cast(int)f; 245 string ret = (cast(int)f).toString; 246 if(isNegative) 247 ret = "-"~ret; 248 249 if(decimal == 0) 250 return ret; 251 ret~= '.'; 252 long multiplier = 10; 253 while(cast(long)(decimal*multiplier) < (decimal*multiplier)) 254 { 255 if(cast(long)(decimal*multiplier) == 0) 256 ret~= '0'; 257 multiplier*=10; 258 } 259 return ret ~ (cast(long)(decimal*multiplier)).toString; 260 } 261 262 pure string toString(void* ptr) @safe nothrow 263 { 264 return ptr is null ? "null" : toHex(cast(size_t)ptr); 265 } 266 267 268 pure string toHex(size_t n) @safe nothrow 269 { 270 enum numbers = "0123456789ABCDEF"; 271 int preAllocSize = 1; 272 ulong div = 16; 273 while(div <= n) 274 { 275 div*= 16; 276 preAllocSize++; 277 } 278 div/= 16; 279 char[] ret = new char[](preAllocSize); 280 int i = 0; 281 282 while(div >= 16) 283 { 284 ret[i++] = numbers[(n/div)%16]; 285 div/= 16; 286 } 287 ret[i] = numbers[n%16]; 288 return ret[0..$]; 289 } 290 291 292 string fromUTF16(wstring str) pure nothrow 293 { 294 string ret; 295 foreach(c;str) ret~= c; 296 return ret; 297 } 298 299 long toInt(string str) pure nothrow @safe @nogc 300 { 301 if(str.length == 0) return 0; 302 str = str.trim; 303 304 ptrdiff_t i = (cast(ptrdiff_t)str.length)-1; 305 306 long last = 0; 307 long multiplier = 1; 308 long ret = 0; 309 if(str[0] == '-') 310 { 311 last++; 312 multiplier*= -1; 313 } 314 for(; i >= last; i--) 315 { 316 if(str[i] >= '0' && str[i] <= '9') 317 ret+= (str[i] - '0') * multiplier; 318 else 319 return ret; 320 multiplier*= 10; 321 } 322 return ret; 323 } 324 325 326 double toFloat(string str) pure nothrow @safe @nogc 327 { 328 if(str.length == 0) return 0; 329 str = str.trim; 330 if(str == "nan" || str == "NaN") return double.init; 331 if(str == "inf" || str == "infinity" || str == "Infinity") return double.infinity; 332 333 int integerPart = 0; 334 int decimalPart = 0; 335 int i = 0; 336 bool isNegative = str[0] == '-'; 337 if(isNegative) 338 str = str[1..$]; 339 bool isDecimal = false; 340 for(; i < str.length; i++) 341 { 342 if(str[i] =='.') 343 { 344 isDecimal = true; 345 continue; 346 } 347 if(isDecimal) 348 decimalPart++; 349 else 350 integerPart++; 351 } 352 353 if(decimalPart == 0) 354 return (isNegative ? -1 : 1) * cast(float)str.toInt; 355 356 i = 0; 357 double decimal= 0; 358 double integer = 0; 359 long integerMultiplier = 1; 360 double floatMultiplier = 1.0f/10.0f; 361 362 while(integerPart > 0) 363 { 364 //Iterate the number from backwards towards the greatest value 365 integer+= (str[integerPart - 1] - '0') * integerMultiplier; 366 integerMultiplier*= 10; 367 integerPart--; 368 i++; 369 } 370 i++; //Jump the . 371 while(decimalPart > 0) 372 { 373 decimal+= (str[i] - '0') * floatMultiplier; 374 floatMultiplier/= 10; 375 decimalPart--; 376 i++; 377 } 378 return (integer + decimal) * (isNegative ? -1 : 1); 379 } 380 381 382 unittest 383 { 384 assert(toString(500) == "500"); 385 assert(toFloat("50.5") == 50.5); 386 assert(toString(100.0) == "100"); 387 assert(toInt("-500") == -500); 388 assert(toString("Hello") == "Hello"); 389 assert(toString(true) == "true"); 390 assert(toString(50.25)== "50.25"); 391 assert(toString(0.003) == "0.003"); 392 assert(toString(0.999) == "0.999"); 393 }